package org.kazuhiko.rpg.client.net.codec;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.kazuhiko.rpg.client.net.RPGSession;
import org.kazuhiko.rpg.client.net.packet.Packet;
import org.kazuhiko.rpg.client.net.packet.PacketDefinition;
import org.kazuhiko.rpg.client.net.packet.PacketField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RPGProtocolDecoder extends CumulativeProtocolDecoder
{
	private static final Logger logger = LoggerFactory.getLogger( RPGProtocolDecoder.class );
	
	private PacketDefinition currentPacket = null;
	
	@Override
	protected boolean doDecode( IoSession session, IoBuffer buffer, ProtocolDecoderOutput out ) throws Exception
	{
		if( currentPacket == null )
		{
			if( buffer.remaining() >= 1 )
			{
				int id = buffer.getUnsigned();
				logger.trace( "Got packet ID {}", id );
				RPGSession sess = new RPGSession( session );
				currentPacket = sess.getPackets().getIncomingPacket( id );
				if( currentPacket == null )
				{
					throw new IOException( "Unknown incoming packet type ( id = " + id + " )." );
				}
			}
			else
			{
				return false;
			}
		}
		
		if( buffer.remaining() >= currentPacket.getLength() )
		{
			Map<String, Object> values = new HashMap<String, Object>();
			
			for( PacketField field : currentPacket.getFields() )
			{
				Object value = null;
				
				switch( field.getType() )
				{
					case BYTE:
						value = buffer.get();
						break;
					case SHORT:
						value = buffer.getShort();
						break;
					case INT:
						value = buffer.getInt();
						break;
					case LONG:
						value = buffer.getLong();
						break;
					case BYTE_ARRAY:
						value = IoBuffer.allocate( 1024 ).put( buffer );
						break;
					case CLASSIC_STRING:
						byte[] bytes = new byte[64];
						buffer.get( bytes );
						value = new String( bytes ).trim();
						break;
					case STRING:
						short length = buffer.getShort();
						byte[] strbytes = new byte[length];
						buffer.get( strbytes );
						value = new String( strbytes ).trim();
						break;
					default:
						//Don't handle the packet
						break;
				}
				
				values.put( field.getName(), value );
			}
			
			Packet packet = new Packet( currentPacket, values );
			currentPacket = null;
			out.write( packet );
			return true;
		}
		
		return false;
	}

}
